#include <stdio.h>
#include <string.h>
#include <stdint.h>

#include "main.h"
#include "Liot_AT.h"

Liot_AT_tcp_status_t g_socket_status[SOCKET_ID_NUM] = {0};

void Liot_AT_socket_callback(uint8_t socket_id, Liot_AT_socket_urc_event_t event, uint8_t *data, uint16_t *len)
{
    switch(event)
    {
        case AT_SOCKET_URC_RECV_DATA:
        {
            if(data == NULL)
            {
                AT_LOGD("Socket recv data\r\n");
            }
            else
            {
                AT_LOGI("Socket[%d] recv: %s\r\n", socket_id, data);
            }
            break;
        }
        case AT_SOCKET_URC_RECV_ERROR:
        {

            break;
        }
        default:break;
    }
}

static void _socket_set_status(uint8_t id, uint8_t enable, Liot_AT_socket_type_t type, Liot_AT_tcp_cfg_t *cfg, uint16_t local_port, Liot_AT_socket_status_t socket_status)
{
    g_socket_status[id].enable = enable;
    g_socket_status[id].type = type;
    if(cfg != NULL)
    {
        memcpy(g_socket_status[id].host, cfg->host, strlen(cfg->host));
        g_socket_status[id].port = cfg->port;
        g_socket_status[id].access_mode = cfg->mode;
    }
    g_socket_status[id].local_port = local_port;
    g_socket_status[id].socket_status = socket_status;
}

uint8_t Liot_check_socket_status(void)
{
    uint8_t i;
    uint8_t status = 0;

    for(i = 0; i < 7; i++)
    {
        status |= (g_socket_status[i].enable << i);
    }

    return status;
}

AT_errorcode_t Liot_AT_get_socket_status(void)
{
    uint8_t cmd[] = "AT+LIPSTATE?\r\n";

    uint32_t socket_id = 0;
    uint8_t service_type[4] = {0};
    uint8_t host[128] = {0};
    uint32_t port = 0;
    uint32_t local_port = 0;
    uint32_t socket_state = 0;
    uint32_t access_mode = 0;

    uint8_t socket_num = 0;
    uint8_t i = 0;

    uint8_t *offset = NULL;
    uint8_t *next = NULL;
    uint8_t *last = NULL;
    uint8_t *buf = NULL;

    Liot_AT_cmd_send(cmd, strlen(cmd));
    if(Liot_AT_wait_rsp(5000) != AT_OK)
    {
        return AT_TIMEOUT;
    }
    if(strstr(g_at_rsp, "ERROR") != NULL)
    {
        Liot_AT_get_rsp_error_reason(g_at_rsp);
        return AT_RECV_ERROR;
    }

    memset(g_socket_status, 0, sizeof(g_socket_status));
    socket_num = AT_find_char_num(g_at_rsp, '+');
    offset = strstr(g_at_rsp, "+LIPSTATE");
    last = offset;
    next = offset;

    for(i = 0 ; i < socket_num ; i++)
    {
        next = strstr(&offset[strlen("+LIPSTATE")], "+LIPSTATE");
        if(next == NULL)
        {
            next = last +strlen(last);
        }
        buf = (uint8_t*)malloc(next - last + 1);
        if(buf == NULL)
        {
            return AT_NO_MEM;
        }
        memset(buf, 0, sizeof(buf));
        memcpy(buf, last, (next - last) );

        Liot_AT_rsp_get_param(buf, "+LIPSTATE", 0, AT_PARAM_TYPE_DEC, &socket_id);
        Liot_AT_rsp_get_param(buf, "+LIPSTATE", 1, AT_PARAM_TYPE_STR, service_type);
        Liot_AT_rsp_get_param(buf, "+LIPSTATE", 2, AT_PARAM_TYPE_STR, host);
        Liot_AT_rsp_get_param(buf, "+LIPSTATE", 3, AT_PARAM_TYPE_DEC, &port);
        Liot_AT_rsp_get_param(buf, "+LIPSTATE", 4, AT_PARAM_TYPE_DEC, &local_port);
        Liot_AT_rsp_get_param(buf, "+LIPSTATE", 5, AT_PARAM_TYPE_DEC, &socket_state);
        Liot_AT_rsp_get_param(buf, "+LIPSTATE", 6, AT_PARAM_TYPE_DEC, &access_mode);

        uint8_t service_type_bit = 0;
        if(strcmp(service_type, "TCP") == 0)
            service_type_bit = AT_SOCKET_TYPE_TCP;
        else
            service_type_bit = AT_SOCKET_TYPE_UDP;

        Liot_AT_tcp_cfg_t cfg = {
            .port = port,
            .mode = access_mode,
        };
				strcpy(cfg.host, host);

        _socket_set_status(socket_id, 1, service_type_bit, &cfg, local_port, socket_state);

        AT_LOGD(" \r\n \
                    \tsocket id : %d\r\n \
                    \tservice type : %s\r\n \
                    \thost : %s\r\n \
                    \tport : %d\r\n \
                    \tlocal port : %d\r\n \
                    \tsocket_state : %d\r\n \
                    \taccess_mode : %d\r\n", socket_id, service_type, host, port, local_port, socket_state, access_mode);

        free(buf);
        last = next;
        offset = next;
    }
    return AT_OK;
}

AT_errorcode_t Liot_AT_tcp_connect(Liot_AT_tcp_cfg_t *cfg, int8_t *id)
{
    uint8_t cmd[128];
    uint8_t *offset = NULL;
    uint32_t socket_id = 0;
    uint32_t result = 0;

    if(cfg == NULL)
    {
        return AT_INVALID_PARAM;
    }
    sprintf(cmd, "AT+LIPOPEN=\"TCP\",\"%s\",%d,%d\r\n", cfg->host, cfg->port, cfg->mode);
    Liot_AT_cmd_send(cmd, strlen(cmd));
    if(Liot_AT_wait_rsp(5000) != AT_OK)
    {
        return AT_TIMEOUT;
    }
    if(strstr(g_at_rsp, "ERROR") != NULL)
    {
        Liot_AT_get_rsp_error_reason(g_at_rsp);
        return AT_RECV_ERROR;
    }
    offset = strstr(g_at_rsp, ":");
    if(offset == NULL)
    {
        if(Liot_AT_wait_rsp(5000) != AT_OK)
        {
            return AT_TIMEOUT;
        }
    }
    offset = strstr(g_at_rsp, ":");
//    sscanf(&offset[2], "%d,%d", &socket_id, &result);
    Liot_AT_rsp_get_param(g_at_rsp, "+LIPOPEN", 0, AT_PARAM_TYPE_DEC, &socket_id);
    Liot_AT_rsp_get_param(g_at_rsp, "+LIPOPEN", 1, AT_PARAM_TYPE_DEC, &result);
    if(result == 1)
    {
        AT_LOGD("Socket Create Successful!\r\n");
        *id = socket_id;
        _socket_set_status(socket_id, 1, AT_SOCKET_TYPE_TCP, cfg, 0, AT_SOCKET_CONNECTED);
        return AT_OK;
    }
    else
    {
        AT_LOGD("Socket Create Failed!\r\n");
        *id = -1;
        return AT_OK;
    }
}


AT_errorcode_t Liot_AT_tcp_close(uint8_t socket_id)
{
    uint8_t cmd[128] = {0};

    sprintf(cmd, "AT+LIPCLOSE=%d\r\n", socket_id);
    Liot_AT_cmd_send(cmd, strlen(cmd));
    if(Liot_AT_wait_rsp(5000) != AT_OK)
    {
        return AT_TIMEOUT;
    }
    if(strstr(g_at_rsp, "ERROR") != NULL)
    {
        Liot_AT_get_rsp_error_reason(g_at_rsp);
        return AT_RECV_ERROR;
    }
    AT_LOGD("Socket Close Successful!\r\n");
    _socket_set_status(socket_id, 0, AT_SOCKET_TYPE_TCP, NULL, 0, AT_SOCKET_INITIAL);
    return AT_OK;
}

AT_errorcode_t Liot_AT_tcp_send(uint8_t socket_id, uint8_t *data, uint16_t len)
{
    uint8_t cmd[1024];
    uint8_t *offset = NULL;
    uint32_t id;
    uint32_t rlen;
    uint32_t result;

    AT_errorcode_t ret = 0;

    if(data == NULL)
    {
        return AT_INVALID_PARAM;
    }
    sprintf(cmd, "AT+LIPSEND=%d,1,%d,\"%s\"\r\n", socket_id, len, data);
    Liot_AT_cmd_send(cmd, strlen(cmd));
    if(Liot_AT_wait_rsp(30000) != AT_OK)
    {
        return AT_TIMEOUT;
    }
    if(strstr(g_at_rsp, "ERROR") != NULL)
    {
        Liot_AT_get_rsp_error_reason(g_at_rsp);
        return AT_RECV_ERROR;
    }
    offset = strstr(g_at_rsp, ":");
    if(offset == NULL)
    {
        if(Liot_AT_wait_rsp(5000) != AT_OK)
        {
            return AT_TIMEOUT;
        }
    }

    Liot_AT_rsp_get_param(g_at_rsp, "+LIPSEND", 0, AT_PARAM_TYPE_DEC, &id);
    Liot_AT_rsp_get_param(g_at_rsp, "+LIPSEND", 1, AT_PARAM_TYPE_DEC, &rlen);
    Liot_AT_rsp_get_param(g_at_rsp, "+LIPSEND", 2, AT_PARAM_TYPE_DEC, &result);
    if(result == 1)
    {
        AT_LOGD("Socket Send Successful!\r\n");
        return AT_OK;
    }
    else
    {
        AT_LOGD("Socket Send Failed!\r\n");
        return AT_OK;
    }
}

AT_errorcode_t Liot_AT_tcp_read(uint8_t socket_id, uint8_t *data, uint16_t maxlen, uint16_t *rlen)
{
    uint8_t cmd[64] = {0};
    uint32_t id = 0;
    uint8_t host[32] = {0};
    uint32_t port = 0;
    uint32_t remaining_length = 0;

    if(data == NULL)
    {
        return AT_INVALID_PARAM;
    }
    sprintf(cmd, "AT+LIPRD=%d,%d\r\n", socket_id, maxlen);
    Liot_AT_cmd_send(cmd, strlen(cmd));
    if(Liot_AT_wait_rsp(5000) != AT_OK)
    {
        return AT_TIMEOUT;
    }
    if(strstr(g_at_rsp, "ERROR") != NULL)
    {
        Liot_AT_get_rsp_error_reason(g_at_rsp);
        return AT_RECV_ERROR;
    }

    Liot_AT_rsp_get_param(g_at_rsp, "+LIPRD", 0, AT_PARAM_TYPE_DEC, &id);
    Liot_AT_rsp_get_param(g_at_rsp, "+LIPRD", 1, AT_PARAM_TYPE_STR, host);
    Liot_AT_rsp_get_param(g_at_rsp, "+LIPRD", 2, AT_PARAM_TYPE_DEC, &port);
    Liot_AT_rsp_get_param(g_at_rsp, "+LIPRD", 3, AT_PARAM_TYPE_DEC, rlen);
    Liot_AT_rsp_get_param(g_at_rsp, "+LIPRD", 4, AT_PARAM_TYPE_DEC, data);
    Liot_AT_rsp_get_param(g_at_rsp, "+LIPRD", 5, AT_PARAM_TYPE_STR, &remaining_length);

    return AT_OK;
}




